vue 组件化 svg

vue 组件化 svg

解决

  • 按需加载,根据使用的 svg 动态生成 svg-sprite
  • 按需添加,可以单独添加自定义的 svg 图标,不用和原有的 svg 图标整合在一起再重新下载。

组件

vue-cli 生成的项目结构为例,在 components/SvgIcon/index.vue 下创建 svg 的组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<template>
<svg :class="svgClass" aria-hidden="true">
<use :xlink:href="iconName"></use>
</svg>
</template>

<script>
export default {
name: "svg-icon",
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String
}
},
computed: {
iconName() {
return `#icon-${this.iconClass}`;
},
svgClass() {
if (this.className) {
return "svg-icon " + this.className;
} else {
return "svg-icon";
}
}
}
};
</script>

<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>

使用 svg-sprite

制作 svg-sprite 。可以使用到 svg-sprite-loader , 它是一个 webpack loader ,可以将多个 svg 打包成 svg-sprite

vue-cli 的基础上进行改造,加入 svg-sprite-loader

vue-cli默认情况下会使用 url-loader 对svg进行处理,会将它放在/img 目录下,所以这时候引入svg-sprite-loader 会引发一些冲突。

1
2
3
4
5
6
7
8
9
//默认`vue-cli` 对svg做的处理,正则匹配后缀名为.svg的文件,匹配成功之后使用 url-loader 进行处理。
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
}

此时会有一个问题,并不是所以的 svg 都是用来当做 icon 的,有些可能只是用来当做图片资源的,并且一些第三方类库可能会使用到 svg

解决方案是使用 webpackexcludeinclude ,让svg-sprite-loader只处理指定文件夹下面的 svgurl-loaer只处理除此文件夹之外的 svg,这样就完美解决了之前冲突的问题。
代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
rules:[{
test: /\.svg$/,
loader: 'svg-sprite-loader',
include: [resolve('src/icons')],
options: {
symbolId: 'icon-[name]'
}
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
exclude:[resolve('src/icons')],
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
}
]

自动导入

src/icons/svg/ 存放所有的单个 svg 图标

利用 webpackrequire.context 将所有 svg 图标自动导出,并将 svg 组件注册为全局组件

src/icons/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
import Vue from "vue";
import SvgIcon from "@/components/SvgIcon"; // svg组件
// import generateIconsView from "@/views/svg-icons/generateIconsView.js"; // just for @/views/icons , you can delete it

// register globally
Vue.component("svg-icon", SvgIcon);

const requireAll = requireContext => requireContext.keys().map(requireContext);
const req = require.context("./svg", false, /\.svg$/);
// const req = require.context("./svg", false, /\.js$/);
const iconMap = requireAll(req);

// generateIconsView.generate(iconMap); // just for @/views/icons , you can delete it

组件中使用

icon-class 为单个 svg 的文件名

1
2
3
<template>
<svg-icon icon-class="svgName" />
</template>
本文结束,感谢您的阅读